package com.erji.common.mybatis.persistence;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.erji.common.mybatis.entity.SuperModel;
import com.erji.common.utils.SnowFlakeSequence;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

public class SuperServiceImpl<M extends SuperMapper<T>, T extends SuperModel> extends ExtServiceImpl implements SuperService<T>, InitializingBean {

    @Autowired
    protected M mapper;
    private Class<T> entityClass;

    public SuperServiceImpl() {
    }

    public void afterPropertiesSet() throws Exception {
        this.entityClass = (Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public T fetchById(String id) {
        return (T) this.mapper.selectById(id);
    }

    public T getById(String id) {
        T obj = this.fetchById(id);
        return obj;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public T updateById(T entity) {
        this.mapper.updateById(entity);
        return entity;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public T save(T entity) {
        if (entity != null) {
            if (StringUtils.isEmpty(entity.getId())) {
                entity.setId(String.valueOf(this.identifierGenerator.nextId((Object)null)));
            }

            this.mapper.insert(entity);
        }

        return entity;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public T saveOrUpdate(T entity) {
        if (entity != null) {
            if (StringUtils.isEmpty(entity.getId())) {
                entity.setId(String.valueOf(this.identifierGenerator.nextId((Object)null)));
                this.mapper.insert(entity);
            } else if (Objects.isNull(this.fetchById(entity.getId()))) {
                this.mapper.insert(entity);
            } else {
                this.mapper.updateById(entity);
            }
        }

        return entity;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        SqlSession batchSqlSession = this.sqlSessionBatch();
        Throwable var4 = null;

        try {
            int i = 0;

            for(Iterator var6 = entityList.iterator(); var6.hasNext(); ++i) {
                T entity = (T) var6.next();
                Thread.sleep(1);
                entity.setId(String.valueOf(new SnowFlakeSequence(1L, 2L).nextId()));
                batchSqlSession.insert(this.sqlStatement(SqlMethod.INSERT_ONE), entity);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
            }

            batchSqlSession.flushStatements();
            return true;
        } catch (Throwable var15) {
            var4 = var15;
            try {
                throw var15;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        } finally {
            if (batchSqlSession != null) {
                if (var4 != null) {
                    try {
                        batchSqlSession.close();
                    } catch (Throwable var14) {
                        var4.addSuppressed(var14);
                    }
                } else {
                    batchSqlSession.close();
                }
            }

        }
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        SqlSession batchSqlSession = this.sqlSessionBatch();
        Throwable var4 = null;

        try {
            int i = 0;

            for(Iterator var6 = entityList.iterator(); var6.hasNext(); ++i) {
                T entity = (T) var6.next();
                if (!StringUtils.isEmpty(entity.getId()) && !Objects.isNull(this.fetchById(entity.getId()))) {
                    ParamMap<T> param = new ParamMap();
                    param.put("et", entity);
                    batchSqlSession.update(this.sqlStatement(SqlMethod.UPDATE_BY_ID), param);
                } else {
                    entity.setId(String.valueOf(this.identifierGenerator.nextId((Object)null)));
                    batchSqlSession.insert(this.sqlStatement(SqlMethod.INSERT_ONE), entity);
                }

                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
            }

            batchSqlSession.flushStatements();
            return true;
        } catch (Throwable var16) {
            var4 = var16;
            throw var16;
        } finally {
            if (batchSqlSession != null) {
                if (var4 != null) {
                    try {
                        batchSqlSession.close();
                    } catch (Throwable var15) {
                        var4.addSuppressed(var15);
                    }
                } else {
                    batchSqlSession.close();
                }
            }

        }
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int removeById(String id) {
        return this.mapper.deleteById(id);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int removeByIds(Collection<String> idList) {
        return this.mapper.deleteBatchIds(idList);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int physicalRemoveById(String id) {
        return this.mapper.physicalDeleteById(id);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int physicalRemoveByIds(Collection<String> idList) {
        return this.mapper.physicalDeleteBatchIds(idList);
    }

    protected T fetch(Wrapper<T> queryWrapper) {
        return (T) this.mapper.selectOne(queryWrapper);
    }

    protected T get(Wrapper<T> queryWrapper) {
        T obj = this.fetch(queryWrapper);
        return obj;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public T update(T entity, Wrapper<T> updateWrapper) {
        int flag = this.mapper.update(entity, updateWrapper);
        if (flag == 0) {
        }

        return entity;
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int remove(Wrapper<T> queryWrapper) {
        return this.mapper.delete(queryWrapper);
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public int physicalRemove(Wrapper<T> queryWrapper) {
        return this.mapper.physicalDelete(queryWrapper);
    }

    protected List<T> find(Wrapper<T> queryWrapper) {
        return this.mapper.selectList(queryWrapper);
    }

    public List<T> findAll() {
        return this.mapper.selectList(new QueryWrapper());
    }

    protected PageInfo<T> findPage(Wrapper<T> queryWrapper, int pageNum, int pageSize) {
        return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> {
            this.mapper.selectList(queryWrapper);
        });
    }

    protected int count(Wrapper<T> queryWrapper) {
        return this.mapper.selectCount(queryWrapper);
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), 1);
    }

    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(this.currentModelClass());
    }

    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.currentModelClass()));
    }

    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(this.currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    public boolean checkPropertyDuplicate(String sourceProperty, String targetProperty, ISelectCount iselect) {
        if (StringUtils.equals(sourceProperty, targetProperty)) {
            return false;
        } else if (Objects.nonNull(iselect)) {
            return iselect.doSelect() > 0L;
        } else {
            return false;
        }
    }
}
